
`include "common_header.verilog"

//  *************************************************************************
//  File : af_fifo_124_sr
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2008 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Peter Riedl, Denis Poverennyy
//  info@morethanip.com
//  *************************************************************************
//  Description : Programmable (Asynchronous / Synchronous) FIFO
//  Version     : $Id: af_fifo_1234wl_sr.v,v 1.2 2017/04/18 08:53:58 fb Exp $
//  *************************************************************************
// 
//    FIFO Name Coding:
//    ----------------
// 
//       1:      Full
//       2:      Almost Full
//       3:      Empty
//       4:      Almost Empty
//       sr:     Synchronous Reset Input
// 
//  *************************************************************************

module af_fifo_1234wl_sr (

   reset_wclk,
   reset_rclk,
   sreset_wclk,
   sreset_rclk,
   wclk,
   wren,
   din,
   rclk,
   rden,
//`ifdef MTIPSDPM_GEN_READEN
//   mem_rden,
//`endif   
   dout,
   afull,
   full,   
   aempty,
   empty,
   wlevel
   
   );

parameter FF_WIDTH      = 4;
parameter ADDR_WIDTH    = 7;
parameter DEPTH         = 128;
parameter AF_THRESHOLD  = 8;
parameter AE_THRESHOLD  = 8;

input   reset_wclk;             // async reset write clock domain
input   reset_rclk;             // async reset read clock domain
input   sreset_wclk;            // sync reset write clock domain (pointer reset)
input   sreset_rclk;            // sync reset read clock domain (pointer reset)
input   wclk;                   // write clock
input   wren;                   // write enable
input   [FF_WIDTH - 1:0] din;   // write data in
input   rclk;                   // read clock
input   rden;                   // read enable (pointer increment)
//`ifdef MTIPSDPM_GEN_READEN
//input   mem_rden;               // memory read enable (normally same as rden)
//`endif   
output  [FF_WIDTH - 1:0] dout;  // read data out
output  afull;                  // almost full (wclk)
output  full;                   // full (wclk)
output  aempty;                 // almost empty (rclk)
output  empty;                  // empty (rclk)
output  [ADDR_WIDTH - 1:0] wlevel;  // write side current level

wire    [FF_WIDTH - 1:0] dout; 
wire    afull;
wire    full; 
wire    aempty;
wire    empty;
wire    [ADDR_WIDTH - 1:0] wlevel;

parameter PTR_WIDTH = ADDR_WIDTH+1;
parameter PTR_DEPTH = DEPTH*2;


wire    [PTR_WIDTH - 1:0] wr_b_ptr; 
wire    [PTR_WIDTH - 1:0] wr_g_ptr; 
wire    [PTR_WIDTH - 1:0] rd_g_wptr; 
wire    [PTR_WIDTH - 1:0] rd_b_wptr; 
reg     [PTR_WIDTH - 1:0] ptr_wck_diff; 

//  Read Clock Domain Signals
//  -------------------------
wire    [PTR_WIDTH - 1:0] rd_b_ptr; 
wire    [PTR_WIDTH - 1:0] rd_g_ptr; 
wire    [PTR_WIDTH - 1:0] wr_g_rptr; 
wire    [PTR_WIDTH - 1:0] wr_b_rptr; 
reg     [PTR_WIDTH - 1:0] ptr_rck_diff; 
reg     afull_flag;
reg     full_flag; 
reg     aempty_flag; 
reg     empty_flag;

// Gray to Binary Conversion
// -------------------------

function [PTR_WIDTH-1:0] gray2bin;

        input [PTR_WIDTH-1:0]  gray_val ;
        
        integer LOOP_IDX1; 
        integer LOOP_IDX2;
begin
        
        gray2bin[PTR_WIDTH - 1] = gray_val[PTR_WIDTH - 1];	
                
        for (LOOP_IDX1 = 0; LOOP_IDX1 <= PTR_WIDTH - 2; LOOP_IDX1 = LOOP_IDX1 + 1)
        begin
         
                gray2bin[LOOP_IDX1] = gray_val[LOOP_IDX1];	

                for (LOOP_IDX2 = PTR_WIDTH - 1; LOOP_IDX2 >= LOOP_IDX1 + 1; LOOP_IDX2 = LOOP_IDX2 - 1)
                begin
            
                        gray2bin[LOOP_IDX1] = gray2bin[LOOP_IDX1] ^ gray_val[LOOP_IDX2];	
            
                end
                
        end
end
endfunction

        
sdpm_gen #(FF_WIDTH,DEPTH,ADDR_WIDTH) SDPRAM (
          .wad(wr_b_ptr[ADDR_WIDTH-1:0]),
          .din(din),
          .wclk(wclk),
          .rclk(rclk),
          .wren(wren),
        `ifdef MTIPSDPM_GEN_READEN
          //.rden(mem_rden),
          .rden(rden),          // same as pointer increment, hence data is valid on next cycle
        `endif   
          .rad(rd_b_ptr[ADDR_WIDTH-1:0]),
          .dout(dout));


gray_cnt_sr #(PTR_WIDTH,PTR_DEPTH) WRCNT (
          .clk(wclk),
          .reset(reset_wclk),
          .sreset(sreset_wclk),
          .enable(wren),
          .b_out(wr_b_ptr),
          .g_out(wr_g_ptr));


gray_cnt_sr #(PTR_WIDTH,PTR_DEPTH) RDCNT (
          .clk(rclk),
          .reset(reset_rclk),
          .sreset(sreset_rclk),
          .enable(rden),
          .b_out(rd_b_ptr),
          .g_out(rd_g_ptr));

mtip_xsync #(PTR_WIDTH) U_WSYNC (

   .data_in(rd_g_ptr),
   .reset(reset_wclk),
   .clk(wclk),
   .data_s(rd_g_wptr));

//  convert back to binary
//  ----------------------------------------------------------------- --
//  Write Clock Domain Path for Full/High Water Mark Generation       --
//  Read Pointer Gray coded value is latched with write clock         --
//  to minimize metastability and since only one bit will change      --
//  the previous value will remain or increment only by one           --
//  ----------------------------------------------------------------- --
assign rd_b_wptr = gray2bin( rd_g_wptr );

//  Asynchronous FIFO, Gray Pointers are Used
//  -----------------------------------------
always @(posedge reset_wclk or posedge wclk)
   begin : ff_aff
   if (reset_wclk == 1'b 1)
      begin
      afull_flag <= 1'b 0;	
      end
   else
      begin
      if (sreset_wclk == 1'b 1)
         begin
         afull_flag <= 1'b 0;	
         end
      else if (ptr_wck_diff >= (DEPTH - AF_THRESHOLD - 1) )
         begin
         afull_flag <= 1'b 1;	
         end
      else
         begin
         afull_flag <= 1'b 0;	
         end
      end
   end

assign afull = afull_flag; 


//  Asynchronous FIFO, Gray Pointers are Used
//  -----------------------------------------
always @(posedge reset_wclk or posedge wclk)
   begin 
   if (reset_wclk == 1'b 1)
      begin
      full_flag <= 1'b 0;	
      end
   else
      begin
      if (sreset_wclk == 1'b 1)
         begin
         full_flag <= 1'b 0;	
         end
      else if( ptr_wck_diff[PTR_WIDTH-1]==1'b 1 ) // extra bit indicates full // if (ptr_wck_diff > (DEPTH - 1) )
         begin
         full_flag <= 1'b 1;	
         end
      else
         begin
         full_flag <= 1'b 0;	
         end
      end
   end

assign full = full_flag; 

//  ----------------------------------------------------------------- --
//  Read Clock Domain Path for Empty/Low Water Mark Generation        --
//  Write Pointer Gray coded value is latched with read clock         --
//  to minimize metastability and since only one bit will change      --
//  the previous value will remain or increment only by one           --
//  ----------------------------------------------------------------- --

mtip_xsync #(PTR_WIDTH) U_RSYNC (

   .data_in(wr_g_ptr),
   .reset(reset_rclk),
   .clk(rclk),
   .data_s(wr_g_rptr));

//  convert to binary value
assign wr_b_rptr = gray2bin( wr_g_rptr );

//  Asynchronous FIFO
//  -----------------
always @(posedge reset_rclk or posedge rclk)
   begin 
   if (reset_rclk == 1'b 1)
      begin
      empty_flag <= 1'b 1;	
      end
   else
      begin
      if (sreset_rclk == 1'b 1)
         begin
         empty_flag <= 1'b 1;	
         end
      else if (ptr_rck_diff == {PTR_WIDTH{1'b 0}} )
         begin
         empty_flag <= 1'b 1;	
         end
      else
         begin
         empty_flag <= 1'b 0;	
         end
      end
   end

assign empty = empty_flag;

always @(posedge reset_rclk or posedge rclk)
   begin 
   if (reset_rclk == 1'b 1)
      begin
      aempty_flag <= 1'b 1;	
      end
   else
      begin
      if (sreset_rclk == 1'b 1)
         begin
         aempty_flag <= 1'b 1;	
         end
      else if (ptr_rck_diff <= (AE_THRESHOLD) )
         begin
         aempty_flag <= 1'b 1;	
         end
      else
         begin
         aempty_flag <= 1'b 0;	
         end
      end
   end

assign aempty = aempty_flag; 

always @(wr_b_ptr or rd_b_wptr)
   begin : process_1
   ptr_wck_diff = wr_b_ptr - rd_b_wptr;	
   end

assign wlevel = ptr_wck_diff[ADDR_WIDTH-1:0];

always @(wr_b_rptr or rd_b_ptr)
   begin : process_2
   ptr_rck_diff = wr_b_rptr - rd_b_ptr;	
   end

endmodule // module a_fifo_124_sr

